#include "Node2D.h"
const std::string Node2D::click("click");
const std::string Node2D::mouseDown("mouseDown");
const std::string Node2D::mouseUp("mouseUp");
const std::string Node2D::mouseMove("mouseMove");
const std::string Node2D::mouseOver("mouseOver");
const std::string Node2D::mouseOut("mouseOut");
const std::string Node2D::mouseWheel("mouseWheel");
Node2D::Node2D(void)
:mName("")
,mParent(NULL)
,mPos(PointZero)
,mAnchorPoint(PointZero)
,mContentSize(SizeZero)
,mAnchorPointInPoints(PointZero)
,mRotation(0)
,mScaleX(1)
,mScaleY(1)
,mSkewX(0)
,mSkewY(0)
,mAlpha(1)
,mVisible(true)
,mDirty(true)
,mColor(ColorWhite)
,mTexcoord(SizeOne)
,mIgnoreAnchorPointForPosition(false)
{
}
Node2D::~Node2D(void)
{
}
void Node2D::update()
{
	//0 1
	//3 2
	if (!isDirty())
	{
		return;
	}
	setDirty(false);
	float alpha = nodeToWorldAlpha();
	Color4B color(mColor.r,mColor.g,mColor.b,alpha);
	float pos_z = 0;
	float left = 0;
	float right = 0 + mContentSize.width;
	float top = 0 + mContentSize.height;
	float bottom = 0;

	mQuad.lt.vertices = Vector3f(left, top, pos_z);
	mQuad.lt.texCoords = Tex2F(0.0f,0.0f);
	mQuad.lt.colors = color;

	mQuad.rt.vertices = Vector3f(right, top, pos_z);
	mQuad.rt.texCoords = Tex2F(mTexcoord.width,  0.0f);
	mQuad.rt.colors = color;

	mQuad.rb.vertices = Vector3f(right,bottom, pos_z);
	mQuad.rb.texCoords = Tex2F(mTexcoord.width,  mTexcoord.height);
	mQuad.rb.colors = color;

	mQuad.lb.vertices = Vector3f(left, bottom, pos_z);
	mQuad.lb.texCoords = Tex2F(0.0f,  mTexcoord.height);
	mQuad.lb.colors = color;

	AffineTransform trans = nodeToWorldTransform();
	doTransfrom(mQuad.lt.vertices,trans);
	doTransfrom(mQuad.rt.vertices,trans);
	doTransfrom(mQuad.lb.vertices,trans);
	doTransfrom(mQuad.rb.vertices,trans);
}
void Node2D::draw(SpriteBatch* spriteBatch)
{

}
void Node2D::doTransfrom(Vector3f& vertices,AffineTransform transform)
{
	Point point(vertices.x,vertices.y);
	point = PointApplyAffineTransform(point,transform);
	vertices.x = point.x;
	vertices.y = point.y;
}
void Node2D::addChild(Node2D* child)
{
	if (isIncludeChild(child))
	{
		return;
	}
	child->setParent(this);
	mChildren.push_back(child);
	setDirty(true);
}
void Node2D::addChild(Node2D* child,int index)
{
	if (isIncludeChild(child))
	{
		return;
	}
	if (index >= count())
	{
		addChild(child);
		return;
	}
	if (index < 0)
	{
		index = 0;
	}
	mChildren.insert(mChildren.begin()+index,child);
	setDirty(true);
}
Node2D* Node2D::getChildAt(int index)
{
	if (index < 0 || index >= count())
	{
		return NULL;
	}
	return mChildren[index];
}
int Node2D::getChildIndex(Node2D* child)
{
	if (child == NULL)
	{
		return -1;
	}
	std::vector<Node2D*>::iterator it;
	int index = 0;
	for (it=mChildren.begin(); it<mChildren.end(); it++ ,index++)
	{
		if ((*it) == child)
		{
			return index;
		}
	}
	return -1;
}
void Node2D::removeChild(Node2D* child)
{
	if (!isIncludeChild(child))
	{
		return;
	}
	std::vector<Node2D*>::iterator it;
	for (it=mChildren.begin(); it<mChildren.end(); it++)
	{
		if ((*it) == child)
		{
			child->setParent(NULL);
			mChildren.erase(it);
			break;
		}
	}
	setDirty(true);
}
void Node2D::removeChildAt(int index)
{
	if (index < 0 || index >= count())
	{
		return;
	}
	std::vector<Node2D*>::iterator it;
	int i = 0;
	for (it=mChildren.begin(); it<mChildren.end(); it++ ,i++)
	{
		if (i == index)
		{
			(*it)->setParent(NULL);
			mChildren.erase(it);
			break;
		}
	}
	setDirty(true);
}
void Node2D::removeAllChildren()
{
	std::vector<Node2D*>::iterator it;
	for (it=mChildren.end(); it<mChildren.begin(); it--)
	{
		(*it)->setParent(NULL);
		(*it)->removeAllChildren();
		mChildren.erase(it);
	}
	setDirty(true);
}
void Node2D::setChildIndex(Node2D* child,int index)
{
	if (!isIncludeChild(child))
	{
		return;
	}
	removeChild(child);
	addChild(child,index);
	setDirty(true);
}
void Node2D::swapChildren(Node2D* child1,Node2D* child2)
{
	if (!isIncludeChild(child1) || !isIncludeChild(child2))
	{
		return;
	}
	int index1 = getChildIndex(child1);
	int index2 = getChildIndex(child2);
	if (index1 > -1 && index2 > -1)
	{
		mChildren[index1] = child2;
		mChildren[index2] = child1;
		setDirty(true);
	}
}
void Node2D::swapChildrenAt(int index1,int index2)
{
	swapChildren(getChildAt(index1),getChildAt(index2));
}
int Node2D::count()
{
	return mChildren.size();
}
bool Node2D::isIncludeChild(Node2D* child,bool recursive)
{
	bool result;
	_isIncludeChild(child,recursive,result);
	return result;
}
void Node2D::_isIncludeChild(Node2D* child,bool recursive,bool& result)
{
	result = false;
	if (child == NULL || child->getParent() == NULL)
	{
		return;
	}
	/*if (child->getParent() != this)
	{
		return;
	}*/
	std::vector<Node2D*>::iterator it;
	for (it=mChildren.begin(); it<mChildren.end(); it++)
	{
		if ((*it) == child)
		{
			result = true;
			return;
		}
		if (recursive)
		{
			(*it)->_isIncludeChild(child,true,result);
		}
	}
}
AffineTransform Node2D::nodeToParentTransform()
{
	float x = mPos.x;
	float y = mPos.y;
	float cr = 1;
	float sr = 0;
	Point anchorPoint(mAnchorPoint.x * mContentSize.width,mAnchorPoint.y * mContentSize.height);
	if (mRotation)
	{
		cr = cosf(mRotation);
		sr = sinf(mRotation);
	}
	if (isIgnoreAnchorPointForPosition())
	{
		x += anchorPoint.x;
		y += anchorPoint.y;
	}
	bool needsSkewMatrix = ( mSkewX || mSkewY );

	if (! needsSkewMatrix && !anchorPoint.equals(PointZero))
	{
		x += cr * -anchorPoint.x * mScaleX + -sr * -anchorPoint.y * mScaleY;
		y += sr * -anchorPoint.x * mScaleX +  cr * -anchorPoint.y * mScaleY;
	}


	AffineTransform  trans  = AffineTransformMake( cr * mScaleX,  sr * mScaleX,
		-sr * mScaleY, cr * mScaleY,
		x, y );

	if (needsSkewMatrix) 
	{
		AffineTransform skewTransform = AffineTransformMake(1.0f,tan(mSkewY),tan(mSkewX),1.0f,0.0f,0.0f);
		trans = AffineTransformConcat(skewTransform, trans);

		if (!anchorPoint.equals(PointZero))
		{
			trans = AffineTransformTranslate(trans, -anchorPoint.x, -anchorPoint.y);
		}
	}
	
	return trans;
}
AffineTransform Node2D::parentToNodeTransform()
{
	return AffineTransformInvert(this->nodeToParentTransform());
}
AffineTransform Node2D::nodeToWorldTransform()
{
	AffineTransform t = this->nodeToParentTransform();
	for (Node2D *p = mParent; p != NULL; p = p->getParent())
		t = AffineTransformConcat(t, p->nodeToParentTransform());
	return t;
}
AffineTransform Node2D::worldToNodeTransform()
{
	 return AffineTransformInvert(this->nodeToWorldTransform());
}
Point Node2D::convertToNodeSpaceAR(const Point& worldPoint)
{
	Point pt = convertToNodeSpace(worldPoint);
	pt.x -= mAnchorPointInPoints.x;
	pt.y -= mAnchorPointInPoints.y;
	return pt;
}
Point Node2D::convertToNodeSpace(const Point& worldPoint)
{
	return PointApplyAffineTransform(worldPoint, worldToNodeTransform());
}
Point Node2D::convertToWorldSpace(const Point& nodePoint)
{
	return PointApplyAffineTransform(nodePoint, nodeToWorldTransform());
}
Point Node2D::convertToWorldSpaceAR(const Point& worldPoint)
{
	Point pt = convertToWorldSpace(worldPoint);
	pt.x += mAnchorPointInPoints.x;
	pt.y += mAnchorPointInPoints.y;
	return pt;
}
float Node2D::nodeToWorldAlpha()
{
	float a = mAlpha;
	for (Node2D *p = mParent; p != NULL; p = p->getParent())
		a*= p->mAlpha;
	return a*255;
}
Rect Node2D::getRect()
{
	Rect rect(mPos.x,mPos.y,mContentSize.width,mContentSize.height);
	return rect;
}
Rect Node2D::getWorldBounds()
{
	AffineTransform trans = nodeToWorldTransform();
	Rect rect = getRect();
	rect.origin.x = 0;
	rect.origin.y = 0;
	rect = RectApplyAffineTransform(rect,trans);
	return rect;
}
bool Node2D::hitTestPoint(Point pt)
{
	return getWorldBounds().containsPoint(pt);
}
bool Node2D::hitTestObject(Node2D* object)
{
	if (object == NULL)
	{
		return false;
	}
	return getWorldBounds().intersectsRect(object->getWorldBounds());
}
std::vector<Node2D*> Node2D::getChildren()
{
	return mChildren;
}
Node2D*	Node2D::getTargetChildAtPosition(Point pt)
{
	std::vector<Node2D*>::const_reverse_iterator it;
	const std::vector<Node2D*>::const_reverse_iterator end = mChildren.rend();
	for (it = mChildren.rbegin(); it != end; it++)
	{
		Node2D* child = (*it);
		if (child->getVisible())
		{
			Node2D* result = child->getTargetChildAtPosition(pt);
			if (result)
			{
				return result;
			}else if (child->hitTestPoint(pt))
			{
				return child;
			}
			
		}
	}
}
void Node2D::setName(std::string name)
{
	//convert const char* to char*
	mName = new char[name.size() + 1];
	std::strcpy ( mName, name.c_str() );
	//mName = const_cast<char*> ( name.c_str() );
}
std::string Node2D::getName()
{
	return mName;
}
void Node2D::setParent(Node2D* parent)
{
	mParent = parent;
	setDirty(true);
}
Node2D* Node2D::getParent()
{
	return mParent;
}
Point Node2D::getPosition()
{
	return mPos;
}
void Node2D::setPosition(Point& pos)
{
	mPos = pos;
	setDirty(true);
}
Point Node2D::getAnchorPoint()
{
	return mAnchorPoint;
}
void Node2D::setAnchorPoint(Point& point)
{
	mAnchorPoint = point;
	Point pt(point.x * mContentSize.width,point.y * mContentSize.height);
	mAnchorPointInPoints = pt;
	setDirty(true);
}
Size& Node2D::getContentSize()
{
	return mContentSize;
}
void Node2D::setContentSize(Size& size)
{
	mContentSize = size;
	setDirty(true);
}
float Node2D::getRotation()
{
	return mRotation;
}
void Node2D::setRotation(float rotation)
{
	mRotation = rotation;
	setDirty(true);
}
float Node2D::getScaleX()
{
	return mScaleX;
}
void Node2D::setScaleX(float scalex)
{
	if (scalex < 0.0f)
	{
		scalex = 0.0f;
	}
	mScaleX = scalex;
	setDirty(true);
}
float Node2D::getScaleY()
{
	return mScaleY;
}
void Node2D::setScaleY(float scaley)
{
	if (scaley < 0.0f)
	{
		scaley = 0.0f;
	}
	mScaleY = scaley;
	setDirty(true);
}
float Node2D::getSkewX()
{
	return mSkewX;
}
void Node2D::setSkewX(float skewx)
{
	if (skewx < 0.0f)
	{
		skewx = 0.0f;
	}else if(skewx > 1.0f)
	{
		skewx = 1.0f;
	}
	mSkewX = skewx;
	setDirty(true);
}
float Node2D::getSkewY()
{
	return mSkewY;
}
void Node2D::setSkewY(float skewy)
{
	if (skewy < 0.0f)
	{
		skewy = 0.0f;
	}else if(skewy > 1.0f)
	{
		skewy = 1.0f;
	}
	mSkewY = skewy;
	setDirty(true);
}
float Node2D::getAlpha()
{
	return mAlpha;
}
void Node2D::setAlpha(float alpha)
{
	mAlpha = alpha;
	setDirty(true);
}
bool Node2D::getVisible()
{
	return mVisible;
}
void Node2D::setVisible(bool visible)
{
	mVisible = visible;
	setDirty(true);
}
bool Node2D::isDirty()
{
	return mDirty;
}
void Node2D::setDirty(bool dirty)
{
	mDirty = dirty;
}
Color4B Node2D::getColor()
{
	return mColor;
}
void Node2D::setColor(Color4B& color)
{
	mColor = color;
	setDirty(true);
}
Size Node2D::getTexCoord()
{
	return mTexcoord;
}
void Node2D::setTexCoord(Size& coord)
{
	mTexcoord = coord;
	setDirty(true);
}
bool Node2D::isIgnoreAnchorPointForPosition()
{
	return mIgnoreAnchorPointForPosition;
}
void Node2D::ignoreAnchorPointForPosition(bool newValue)
{
	if (newValue != mIgnoreAnchorPointForPosition) 
	{
		mIgnoreAnchorPointForPosition = newValue;
		setDirty(true);
	}
}
void Node2D::onClick(MouseEventArgs& e)
{
}
void Node2D::onMouseOver(MouseEventArgs& e)
{
	dispatchEvent(mouseOver,this,e);
}
void Node2D::onMouseOut(MouseEventArgs& e)
{
	dispatchEvent(mouseOut,this,e);
}
void Node2D::onMouseWheel(MouseEventArgs& e)
{
	dispatchEvent(mouseWheel,this,e);
	for (Node2D *p = mParent; p != NULL; p = p->getParent())
		p->dispatchEvent(mouseWheel,this,e);
}
void Node2D::onMouseDown(MouseEventArgs& e)
{
	dispatchEvent(mouseDown,this,e);
	for (Node2D *p = mParent; p != NULL; p = p->getParent())
		p->dispatchEvent(mouseDown,this,e);
}
void Node2D::onMouseUp(MouseEventArgs& e)
{
	dispatchEvent(mouseUp,this,e);
	for (Node2D *p = mParent; p != NULL; p = p->getParent())
		p->dispatchEvent(mouseUp,this,e);
}
void Node2D::onMouseMove(MouseEventArgs& e)
{
	dispatchEvent(mouseMove,this,e);
	for (Node2D *p = mParent; p != NULL; p = p->getParent())
		p->dispatchEvent(mouseMove,this,e);
}
void Node2D::onCharacter(KeyEventArgs& e)
{
	std::vector<Node2D*>::iterator it;
	for (it=mChildren.begin(); it<mChildren.end(); it++ )
	{
			(*it)->onCharacter(e);
	}
}